Electron equivalent ellipse calculation

This calculates the length and width of the equivalent ellipse of cutouts defined appropriately within shape_definition_x_coords.csv and shape_definition_y_coords.csv. The measured cutout factors defined within measured_cutout_factors.csv are also used to calculate the equivalent ellipse.

The widths and lengths are then output into measured_cutout_factors.csv for the input into Electron Spline Modelling.ipynb.


In [ ]:


In [ ]:

Loading/installing modules


In [2]:
try:
    import shapely.geometry as geo
    import shapely.affinity as aff
    
except:
    !pip install shapely
    import shapely.geometry as geo
    import shapely.affinity as aff
    

try:
    from descartes.patch import PolygonPatch
    
except:
    !pip install descartes
    from descartes.patch import PolygonPatch
    

import numpy as np
import pandas as pd

import matplotlib.pyplot as plt
from matplotlib import pylab
%matplotlib inline

from scipy.optimize import basinhopping
from scipy.interpolate import UnivariateSpline

import os

In [2]:

Importing input data


In [4]:
shape_definition_x_coords = pd.read_csv("shape_definition_x_coords.csv")
shape_definition_y_coords = pd.read_csv("shape_definition_y_coords.csv")

measured_factors = pd.read_csv("measured_factors.csv")

In [ ]:

Class definitions

Equivalent ellipse fitting


In [58]:
class equivalent_ellipse(object):
    """An equivalent ellipse given the x and y coordinates of a cutout.
    """
    def __init__(self, n=5, debug=False, **kwargs):
        
        self.debug = debug
        
        self.cutoutXCoords = kwargs['x']
        self.cutoutYCoords = kwargs['y']
        self.cutout = self.shapely_cutout()
                
        self.basinRequiredSuccess = n
        self.ellipseRaw = self.ellipse_basinhopping()
        
        self.width = self.ellipseRaw[2]
        self.length = self.ellipseRaw[3]
        
        self.ellipse = self.shapely_ellipse(self.ellipseRaw)
    
    
    def shapely_cutout(self):
        """Returns the shapely cutout defined by the x and y coordinates.
        """
        return geo.Polygon(np.transpose((self.cutoutXCoords,self.cutoutYCoords)))
    
    
    def shapely_ellipse(self,ellipseRaw):
        """Given raw ellipse values create a shapely ellipse.
        """
        xPosition = ellipseRaw[0]
        yPosition = ellipseRaw[1]
        
        width = ellipseRaw[2]
        length = ellipseRaw[3]
        
        rotation = ellipseRaw[4]
        
        
        unitCircle = geo.Point(0,0).buffer(1)        
        ellipse = aff.scale(unitCircle, xfact=width/2, yfact=length/2)    # Stretched
        ellipse = aff.translate(ellipse, xoff=xPosition, yoff=yPosition)  # Translated
        ellipse = aff.rotate(ellipse, rotation)                           # Rotated
        
        return ellipse
    
    
    def minimise_function(self, ellipseRaw):
        """Returns the sum of area differences between the an ellipse and the given cutout.
        """
        ellipse = self.shapely_ellipse(ellipseRaw)
        
        return ellipse.difference(self.cutout).area + self.cutout.difference(ellipse).area
        
    
    def ellipse_basinhopping(self):
        """Fitting the ellipse to the cutout via scipy.optimize.basinhopping.
        """
        self.functionReturns = np.empty(self.basinRequiredSuccess)
        self.functionReturns[:] = np.nan
        
        self.numSuccess = 0
        
        minimizerConfig = {"method": 'BFGS'}
        
        initial_input = np.array([0,0,3,4,0])
        
        
        basinhoppingOutput = basinhopping(self.minimise_function,
                                          initial_input,
                                          niter=1000,
                                          minimizer_kwargs=minimizerConfig,
                                          take_step=self.step_function,
                                          callback=self.callback_function)
        
        return basinhoppingOutput.x
        
        
    def step_function(self,optimiserInput):
        """Step function used by self.ellipse_basinhopping.
        """
        optimiserInput[0] += np.random.normal(scale=1.5)   # x-position
        optimiserInput[1] += np.random.normal(scale=1.5)   # y-position
        optimiserInput[2] += np.random.normal(scale=3)     # width
        optimiserInput[3] += np.random.normal(scale=4)     # length
        optimiserInput[4] += np.random.normal(scale=90)    # rotation
        
        return optimiserInput
    
    
    def callback_function(self, optimiserOutput, minimiseFunctionOutput, minimiseAccepted):
        """Callback function used by self.ellipse_basinhopping.
        """        
        if self.debug:
            print(optimiserOutput)
            print(minimiseFunctionOutput)
            print(minimiseAccepted)
            print(" ")
        
        if minimiseAccepted:
            
            if self.numSuccess == 0:
                # First result
                self.functionReturns[0] = minimiseFunctionOutput
                self.numSuccess = 1
                
            elif minimiseFunctionOutput >= np.nanmin(self.functionReturns) + 0.0001:
                # Reject result
                0
                
            elif minimiseFunctionOutput >= np.nanmin(self.functionReturns) - 0.0001:
                # Agreeing result
                self.functionReturns[self.numSuccess] = minimiseFunctionOutput
                self.numSuccess += 1
            
            elif minimiseFunctionOutput < np.nanmin(self.functionReturns) - 0.0001:
                # New result
                self.functionReturns[0] = minimiseFunctionOutput
                self.numSuccess = 1
        
        if self.numSuccess >= self.basinRequiredSuccess:
            return True

Create bivariate spline fit


In [ ]:

Finding centre with sector integration


In [4]:

Shape straightening


In [ ]:


In [ ]:

Main


In [ ]:


In [48]:
test_x = shape_definition_x_coords["Simon #3 cutout"].values


test_x = test_x[~np.isnan(test_x)]
test_x


Out[48]:
array([-0.3 , -0.25,  0.75,  1.05,  1.15,  1.35,  1.45,  1.85,  1.95,
        2.2 ,  2.2 ,  2.3 ,  2.3 ,  2.4 ,  2.4 ,  2.3 ,  2.3 ,  2.2 ,
        2.2 ,  2.1 ,  2.1 ,  2.  ,  2.  ,  1.9 ,  1.9 ,  1.6 ,  1.6 ,
        1.35,  1.05,  0.85,  0.05, -0.05, -0.95, -1.15, -1.35, -1.8 ,
       -1.8 , -1.9 , -1.9 , -2.  , -2.  , -2.1 , -2.1 , -2.  , -2.  ,
       -1.9 , -1.9 , -1.7 , -1.7 , -1.6 , -1.6 , -1.4 , -1.4 , -1.3 ,
       -1.3 , -1.2 , -1.2 , -0.55, -0.45, -0.3 ])

In [49]:
test_y = shape_definition_y_coords["Simon #3 cutout"].values


test_y = test_y[~np.isnan(test_y)]
test_y


Out[49]:
array([ 2.85,  2.9 ,  2.9 ,  2.6 ,  2.6 ,  2.4 ,  2.4 ,  2.  ,  2.  ,
        1.75,  1.45,  1.35,  1.05,  0.95, -0.85, -0.95, -1.25, -1.35,
       -1.55, -1.65, -1.75, -1.85, -1.95, -2.05, -2.15, -2.45, -2.55,
       -2.8 , -2.8 , -3.  , -3.  , -2.9 , -2.9 , -2.7 , -2.7 , -2.25,
       -1.95, -1.85, -1.55, -1.45, -1.35, -1.25, -0.15, -0.05,  0.15,
        0.25,  0.55,  0.75,  0.95,  1.05,  1.35,  1.55,  1.65,  1.75,
        1.85,  1.95,  2.05,  2.7 ,  2.7 ,  2.85])

In [59]:
test = equivalent_ellipse(n=2,debug=True,x=test_x,y=test_y)


[  0.18675975  -0.17073122   6.03573818   4.53605462 -98.46253108]
1.2234061270002186
True
 
[  1.86759722e-01  -1.70731240e-01   4.53605471e+00   6.03573780e+00
  -1.88462535e+02]
1.2234061270011085
True
 

In [66]:
plt.plot(test.cutout.exterior.xy[0],test.cutout.exterior.xy[1])
plt.plot(test.ellipse.exterior.xy[0],test.ellipse.exterior.xy[1])

plt.axis("equal")


Out[66]:
(-3.0, 3.0, -4.0, 3.0)

In [61]:
test.ellipse


Out[61]:

In [62]:
test.cutout


Out[62]:

In [ ]:


In [12]:
a = np.random.normal(size=10)
b = np.random.normal(size=10)

In [13]:
a


Out[13]:
array([ 2.61858993,  0.56417145, -0.37544616, -0.21942874, -0.46881228,
        0.77196593, -0.15791087, -1.0903846 , -0.65885628,  0.32071251])

In [14]:
b


Out[14]:
array([-0.01860668, -0.36449584, -0.62044261,  0.37710933,  0.01426758,
        0.31517226,  1.51211952, -0.15872554,  0.85998421, -1.14476894])

In [31]:
list(tuple(np.transpose([a,b])))


Out[31]:
[array([ 2.61858993, -0.01860668]),
 array([ 0.56417145, -0.36449584]),
 array([-0.37544616, -0.62044261]),
 array([-0.21942874,  0.37710933]),
 array([-0.46881228,  0.01426758]),
 array([ 0.77196593,  0.31517226]),
 array([-0.15791087,  1.51211952]),
 array([-1.0903846 , -0.15872554]),
 array([-0.65885628,  0.85998421]),
 array([ 0.32071251, -1.14476894])]

In [39]:
geo.Polygon(np.transpose((a,b)))


WARNING:shapely.geos:Self-intersection at or near point -0.26858277602067238 0.062825813964053248
Out[39]:

In [ ]: